#!/usr/bin/env bash

function test_main()
{
    if [ "$OHOS_TOOL_TEST" != "true" ]; then
        define_empty_function test:run_tests
        define_empty_function test:register_test
        return
    fi

    declare -a test_maps
    declare command_output
    declare expect_total expect_success
    declare test_total test_success
    declare errors
    declare snapshot

    function test:is_manual_test()
    {
        [[ "$test" =~ (:manual$) ]]
    }

    function test:register_test()
    {
        test_maps+=($*)
    }

    function test:run_tests()
    {
        local debug=false manual=false
        local allows=()

        arguments $*
        while arguments:have_next; do
            if arguments:is_option; then
                case "$(arguments:parse_option)" in
                    manual)
                        manual=true
                        ;;
                    debug)
                        debug=true
                        ;;
                    *)
                        echo "not support $(arguments:get_now)"
                        ;;
                esac
                arguments:next
            else
                allows+=($(arguments:get_now))
                arguments:next
            fi
        done

        function test:run_tests:before1()
        {
            if test:is_manual_test; then
                echo "    running test $(cctl c)$test$(cctl 0)"
                return
            fi
            echo "    running test $(cctl c)$test$(cctl 0) {"
            printf "        "

            ((test_total++))
            expect_total=0
            expect_success=0
            errors=()
        }

        function test:run_tests:before2()
        {
            [ "$1" == "debug" ] && set -x
        }

        function test:run_tests:after2()
        {
            set +x
        }

        function test:run_tests:after1()
        {
            test:is_manual_test && return
            printf "\n"
            for (( i = 0; i < ${#errors[*]}; i++ )); do
                echo -e "        $(cctl y)${errors[$i]}$(cctl 0)"
            done
            printf "    } "

            if [ "$expect_total" = "$expect_success" ]; then
                ((test_success++))
                printf "$(cctl g)"
            else
                printf "$(cctl r)"
            fi
            echo "($expect_success/$expect_total)$(cctl 0)"
        }

        echo "running $((${#manual_test_maps[*]} + ${#test_maps[*]})) tests"
        echo "{"

        for test in ${test_maps[*]}; do
            if test:is_manual_test; then
                if [ "${#allows[*]}" == "0" ] || [ "$manual" == "false" ]; then
                    continue
                fi
            fi

            if [ "${#allows[*]}" != "0" ]; then
                can=false
                for allow in ${allows[*]}; do
                    if [[ "$test" =~ $allow ]]; then
                        can=true
                        break
                    fi
                done
                if [ "$can" != "true" ]; then
                    echo "    skip test $(cctl c)$test$(cctl 0)"
                    continue
                fi
            fi

            test:run_tests:before1 $*
            test:run_tests:before2 $*
            $test
            test:run_tests:after2 $*
            test:run_tests:after1 $*
        done

        echo "}"
        echo "running tests finish, ($test_success/$test_total)"
    }

    function test:run_cmd()
    {
        command_output="$($*)"
    }

    function test:run_snapshot()
    {
        snapshot="$*"
        snapshot="${snapshot//:/_}"
        snapshot="snapshot/${snapshot// /_}"
        if [ ! -e "$snapshot" ]; then
            echo "generate snapshot at $snapshot"
            $* | xxd > $snapshot
        else
            test:run_cmd $*
            result=$(xxd -r $snapshot)
            test:should_be "$result"
        fi
    }

    function test:should_be()
    {
        ((expect_total++))
        local left_md5sum="$(md5sum <<< "$command_output")"
        local right_md5sum="$(md5sum <<< "$1")"
        if [ "$left_md5sum" == "$right_md5sum" ]; then
            printf "$(cctl g).$(cctl 0)"
            ((expect_success++))
        else
            printf "$(cctl r)x$(cctl 0)"
            errors+=("test:should_be ('$command_output' != '$1')")
            if (( ${#command_output} >= 32 )); then
                errors+=("('$left_md5sum' != '$right_md5sum')")
                xxd <<< "$command_output" > /tmp/1
                xxd <<< "$1" > /tmp/2
                errors+=("diff: $(icdiff /tmp/1 /tmp/2)")
            fi
        fi
    }

    function test:must_be()
    {
        if [ "$command_output" != "$1" ]; then
            echo "$(cctl r)test:must_be ('$command_output' != '$1')$(cctl 0)"
            exit 1
        fi
    }

    function test:should_return()
    {
        local retval="$1"
        shift
        ((expect_total++))
        $* 1>/dev/null 2>&1
        local rv=$?
        if [ "$retval" == "-1" ] && [ "$rv" != "0" ]; then
            rv="-1"
        fi
        if [ "$rv" == "$retval" ]; then
            printf "$(cctl g).$(cctl 0)"
            ((expect_success++))
        else
            errors+=("test:should_return $retval ('$*' return $rv)")
            printf "$(cctl r)x$(cctl 0)"
        fi
    }

    function test:should_return_true()
    {
        test:should_return 0 $*
    }

    function test:should_return_false()
    {
        test:should_return -1 $*
    }

    function test:var_should_be()
    {
        ((expect_total++))
        if [ "${!1}" == "$2" ]; then
            printf "$(cctl g).$(cctl 0)"
            ((expect_success++))
        else
            printf "$(cctl r)x$(cctl 0)"
            errors+=("test:var_should_be ('\$$1'{${!1}} != '$2')")
        fi
    }
}

test_main
include "libs/arguments.sh"
